﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using InnovTech.Persistence;
using NHibernate.Cfg;

namespace NHibernate.Util
{
    public abstract class NHibernateVirtualDatabaseEnvironmentBase<TDatabaseInfo> : IVirtualDatabaseEnvironment<TDatabaseInfo>
        where TDatabaseInfo : IDatabaseInfo
    {
        ISessionFactory _factory;
        NHibernateSessionUnitWorker _unitWorker;
        NHibernateDatabaseGeneratorBase<TDatabaseInfo> _databaseGenerator;

        public NHibernateVirtualDatabaseEnvironmentBase()
        {
        }

        /// <summary>
        /// Gets the current used IDatabaseInfo
        /// </summary>
        public TDatabaseInfo DatabaseInfo
        {
            get;
            protected set;
        }

        /// <summary>
        /// Gets the NHibernate Configuration which is used by this object.
        /// </summary>
        public Configuration Config
        {
            get
            {
                return DatabaseGenerator.GetConfiguration(DatabaseInfo);
            }
        }

        /// <summary>
        /// Gets the ISessionFactory used to open ISession.
        /// </summary>
        ISessionFactory SessionFactory
        {
            get
            {
                if (_factory == null)
                    _factory = Config.BuildSessionFactory();
                return _factory;
            }
        }
        NHibernateDatabaseGeneratorBase<TDatabaseInfo> DatabaseGenerator
        {
            get
            {
                if (_databaseGenerator == null)
                    _databaseGenerator = CreateDatabaseGenerator();
                return _databaseGenerator;
            }
        }

        NHibernateSessionUnitWorker UnitWorker
        {
            get
            {
                if (_unitWorker == null)
                    _unitWorker = new NHibernateSessionUnitWorker(SessionFactory);
                return _unitWorker;
            }
        }
        /// <summary>
        /// Indicates whether the current database environment is running.
        /// </summary>
        public bool IsRunning
        {
            get
            {
                return DatabaseInfo != null;
            }
        }

        /// <summary>
        /// Gets the NHibernateUnitWorker instance.
        /// </summary>
        /// <param name="interceptor"></param>
        /// <returns></returns>
        public NHibernateSessionUnitWorker GetUnitWorker(IInterceptor interceptor)
        {
            UnitWorker.Interceptor = interceptor;
            return UnitWorker;
        }

        
        /// <summary>
        /// Starts the database environment
        /// </summary>
        /// <param name="databaseInfo"></param>
        public void Start(TDatabaseInfo databaseInfo)
        {
            if (IsRunning) throw new Exception("The virtual database environment is already running");
            DatabaseInfo = databaseInfo;
            DatabaseGenerator.SetDatabaseInfo(DatabaseInfo);
            DatabaseGenerator.GenerateDatabase();
        }
        /// <summary>
        /// Stops the running database environment.
        /// </summary>
        public void Stop()
        {
            if (!IsRunning) throw new Exception("The virtual database environment is not running");
            MultiUserSingleConnectionProvider.ExplicitlyDestroyConnection();
            Cleanup(DatabaseInfo);
            DatabaseInfo = default(TDatabaseInfo);
        }
        /// <summary>
        /// Opens an ISession instance
        /// </summary>
        /// <param name="interceptor"></param>
        /// <returns></returns>
        public ISession OpenSession(IInterceptor interceptor = null)
        {
            return interceptor == null ? SessionFactory.OpenSession() :
                SessionFactory.OpenSession(interceptor);
        }
        /// <summary>
        /// Generates the database-schema as sql script.
        /// </summary>
        /// <returns></returns>
        public string GenerateSqlScript()
        {
            return DatabaseGenerator.GenerateSqlScript();
        }

        /// <summary>
        /// Cleans up after closing the database environment.
        /// </summary>
        /// <param name="databaseInfo"></param>
        protected abstract void Cleanup(TDatabaseInfo databaseInfo);
        /// <summary>
        /// Creates a NHibernateDatabaseGeneratorBase to create the database
        /// </summary>
        /// <returns></returns>
        protected abstract NHibernateDatabaseGeneratorBase<TDatabaseInfo> CreateDatabaseGenerator();

        
    }
}
